/*********************************************************************************
 *      Copyright:  (C) 2023 LXC
 *                  All rights reserved.
 *
 *       Filename:  socket.c
 *    Description:  This file socket.c
 *                 
 *        Version:  1.0.0(10/05/23)
 *         Author:  Lin XinCheng <1481155734@qq.com>
 *      ChangeLog:  1, Release initial version on "10/05/23 08:11:39"
 *                 
 ********************************************************************************/
#include <time.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "socket.h"
#include "sqlite.h"
#include "logger.h"

//初始化socket 
int socket_init(socket_t *sock, char *hostname, int port)
{
	if ( (NULL == sock) || (NULL == hostname) || (port < 0) )
	{
		return -1;
		LOGGER_ERROR("NULL pointer");
	}
	memset(sock, 0, sizeof(sock));

	strncpy(sock->host, hostname, sizeof(sock->host));//服务器主机名ip 
	sock->port = port;	//服务器端口
	LOGGER_DEBUG("socket_init successful");
	return 1;
}

//socket连接
int socket_connect(socket_t *sock)
{
	int					rv = -1;		
	int					status;			
	struct addrinfo 	hints;			
	memset(&hints, 0, sizeof(hints));
	struct addrinfo  	*res;			
	struct addrinfo 	*ip_buf;		
	hints.ai_family = AF_UNSPEC;		
	hints.ai_socktype = SOCK_STREAM;	

	if ( NULL == sock )
	{
		LOGGER_ERROR("NULL pointer");
		goto cleanup;
	}

	if ( (status = getaddrinfo(sock->host, NULL, &hints, &res)) != 0 )
	{	
        /* 域名解析失败 */
		LOGGER_ERROR("getaddrinfo [%s] failure: %s", sock->host, gai_strerror(status));
		goto cleanup;
	}
	for (ip_buf = res; ip_buf != NULL; ip_buf = ip_buf->ai_next)
	{	
        /* 域名解析成功，遍历解析后的ip地址并尝试连接，直到成功或全部连接失败 */
		if ( ip_buf->ai_family == AF_INET )
		{	
			struct sockaddr_in *ipv4 = (struct sockaddr_in *)ip_buf->ai_addr; /* 如果解析出来是ipv4地址 */
			ipv4->sin_port = htons(sock->port);
			
		}
		else if ( ip_buf->ai_family == AF_INET6 )
		{	
			struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ip_buf->ai_addr;	/* 如果解析出来是ipv6地址 */
			ipv6->sin6_port = htons(sock->port);
		}
		if ( (sock->fd = socket(ip_buf->ai_family, SOCK_STREAM, 0)) < 0 )
		{	
            /* 创建对应ip地址类型的socket */
			LOGGER_ERROR("socket failure: %s", strerror(errno));
			goto cleanup;
		}
		LOGGER_DEBUG("socket [%d] successfully", sock->fd);

		/* 尝试连接socket */
		rv = connect(sock->fd, (struct sockaddr *)ip_buf->ai_addr, sizeof(struct sockaddr));
		if ( rv <= 0 )
		{
			sock->connected = 0;	/* 连接失败 */
			socket_close(sock);		/* 关闭socket */
			LOGGER_ERROR("connect to [%s:%d] failure: %s", sock->host, sock->port, strerror(errno));
			continue;
		}
		else
		{
			/* 连接成功 */
			sock->connected = 1;
			LOGGER_DEBUG("connect to [%s:%d] successfully", sock->host, sock->port);
			break;
		}
	}

cleanup:
	freeaddrinfo(res);
	return sock->connected;
}


//判断和设置socket连接标志 
int socket_diag(socket_t *sock)
{
	int 		error;
	socklen_t 	len = sizeof(error);

	if ( NULL == sock )
	{
		return -1;
		LOGGER_ERROR("NULL pointer");
	}

	/* 判断连接情况 */
	getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
	if ( (1 == sock->connected) && (0 == error) )
	{
		sock->connected = 1;
		LOGGER_DEBUG("diag: connected\n");
		return 1;
	}
	else
	{
		sock->connected = 0;
		LOGGER_DEBUG("diag：connect failure\n");
		return 0;
	}
}

//写入数据 
int socket_write(socket_t *sock, char *data, int bytes)
{
	int	wv = 0;		//发送成功的字节数

	if ( (NULL == sock) || (NULL == data) )
	{
		return -1;
		LOGGER_ERROR("NULL pointer");
	}

	while( bytes > 0 )
	{	//如果要发送的字节数大于1024，则分次发，每次只发1024字节，若小于1024字节则一次性全部发送
		wv = write(sock->fd, data, (bytes >= 1024) ? 1024 : bytes);
		if ( wv <= 0 )
		{	
			LOGGER_ERROR("write failure: %s", strerror(errno));
			return 0;
		}
		bytes -= wv; //未发送的字节数等于总字节数的减去已发送的字节数
	}
	return 1;
}

/* 读取数据 */
int socket_read(socket_t *sock, char *data, int bytes)
{
	int			rv = 0;

	if ( (NULL == sock) || (NULL == data) )
	{
		return -1;
		LOGGER_ERROR("NULL pointer");
	}

	rv = read(sock->fd, data, bytes);
	if ( rv <= 0 )
	{	
		LOGGER_ERROR("read failure: %s", strerror(errno));
		return 0;
	}
	return 1;
}

//关闭socket
int socket_close(socket_t *sock)
{
	if ( NULL == sock )
	{
		return 0;
		LOGGER_ERROR("NULL pointer");
	}
	close(sock->fd);
	sock->fd = -1;
	return 1;
}